<script setup>
import { reactive, unref, ref, onMounted } from 'vue'

const waterfall = ref(null)
let domList = []

const state = reactive({
  // 放图片的列表
  waterfallList: [],
  // 列数
  waterfallCol: 3,
  // 定宽
  colWidth: 0,
  // 间距
  marginRight: 10,
  marginBottom: 10,
  // 高度数组, 保存每一列的高度, 用于渲染图片(每一行图片都需要渲染到高度最低的那一列)
  colHeights: [],
  // 是否加载
  // loading: false,
  apiIndex: 0
})

onMounted(() => {
  init()
})

const getImgs = (el) => {
  domList.push(el)
}

const init = () => {
  // 初始化时，每栏高度都为0
  // 创建几列
  state.colHeights = new Array(state.waterfallCol)
  for (let i = 0; i < state.colHeights.length; i++) {
    // 初始化高度数组为0
    state.colHeights[i] = 0
  }
  // 计算列宽, 计算方法: (容器宽度 - 几个边距) / 列数
  state.colWidth = (unref(waterfall).clientWidth - (state.waterfallCol - 1) * state.marginRight) / state.waterfallCol
  
  // 去执行方法了
  loadImgs(state.apiIndex)
}

// 加载图片对象, 只是图片对象, 还不是dom
const loadImgs = (idx) => {
  let imgList = []
  for(let i = idx; i < idx + 3; i++) {
    imgList.push({
      image_url: '../../../src/images/'+(i+1)+'.png'
    })
  }
  
  // 返回的图片数组
  let images = imgList
  // n个promise
  let promiseAll = [],
      // 存放图片对象的数组
      imgs = [],
      // 图片总数, 用于下面遍历
      total = images.length

  for (let i = 0; i < total; i++) {
    promiseAll[i] = new Promise(resolve => {
      // 图片对象
      imgs[i] = new Image()
      imgs[i].src = images[i].image_url
      // 图片加载完成, 此时图片的宽高已经有值了
      imgs[i].onload = () => {
        // 存放图片数据
        let imgData = {}
        imgData.height = (imgs[i].height * state.colWidth) / imgs[i].width
        imgData.width = state.colWidth
        imgData.src = images[i].image_url
        // 随后集成宽, 高, src等信息的对象放在待渲染的列表中
        state.waterfallList.push(imgData)
        // 调用该方法, 计算偏移量, 同样封装到这个对象中
        rankImgs(imgData)
        // 返回成功回调
        resolve(imgs[i])
      }
    })
  }

  // 执行所有promise, 在这里执行观察者, 以实现懒加载(也可稍作修改实现预加载)
  Promise.all(promiseAll).then(() => {
    observe()
  })
}

const rankImgs = (imgData) => {
  // 得到最小高度以及对应的索引
  let min = filterMin()
  // 偏移量计算
  imgData.top = min.minHeight
  imgData.left = min.minIndex * (state.colWidth + state.marginRight)
  // 更新高度数组, 图片渲染上去后高度数组需要更新
  state.colHeights[min.minIndex] += imgData.height + state.marginBottom
}

// 返回最小高度
const filterMin = () => {
  // 返回数组中的最小值, 这里的apply用法可以看我上一篇笔记
  let minHeight = Math.min.apply(null, state.colHeights)
  // 返回一个对象, 包含最小高度, 最小高度对应的索引
  return {
    minHeight: minHeight,
    minIndex: state.colHeights.indexOf(minHeight)
  }
}

const observe = () => {
  let imgs = document.getElementsByClassName('wf-item')
  // let imgs = domList

  let index = state.waterfallList.length - (state.waterfallList.length % state.waterfallCol) - state.waterfallCol + 1
  
  console.log(index)

  const obser = new IntersectionObserver((entries) => {
    // console.log(entries[0].intersectionRatio)
    if (entries[0].intersectionRatio > 0) {
      // console.log(entries[0].intersectionRatio)
      // unobserve()停止观察某个对象
      obser.unobserve(imgs[index])
      //disconnect停止观察, 观察者失效
      // observe.disconnect(imgs[index])
      // 重新执行load方法, 且这个观察方法也是在那里调用的, 会增加新的观察对象
      state.apiIndex += 3
      if(state.apiIndex > 36) {
        obser.disconnect()
      } else {
        loadImgs(state.apiIndex)
      }
    }
  })
  console.log(imgs[index])
  // 观察
  obser.observe(imgs[index])
}

</script>

<template>
  <div class="waterfall wf-wrap" ref="waterfall">
    <TransitionGroup name="list" tag="ul">
      <li
        v-for="(item, index) in state.waterfallList"
        :key="index"
        class="wf-item"
        :ref="getImgs"
        :style="{
          top: item.top + 'px',
          left: item.left + 'px',
          width: item.width + 'px',
          height: item.height + 'px',
        }"
      >
        <img :src="item.src" class="img-item" />
      </li>
    </TransitionGroup>
  </div>
</template>
  
<style scoped>

ul li {
  list-style: none;
}

.wf-wrap {
  padding-top: 4rem;
  width: 500px;
  position: relative;
  margin: 0 auto;
}
.wf-item {
  position: absolute;
  height: 100%;
}
.wf-item img {
  width: 100%;
  height: 100%;
  border: 1px solid #ddd;
}

/*定义渲染动画*/
.list-enter-active, .list-leave-active {
  transition: all 1s ease;
}
.list-enter-from, .list-leave-to {
  opacity: 0;
  transform: translateY(30px);
}
</style>
  
  